/******************************************************************************
 * @file     system_link32fa016bx.c
 * @brief    Device Peripheral Access Layer Source File for Device link32fa016bx
 * @version  V1.00
 * @date     27. Aug 2023
 ******************************************************************************/
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include "link32fa016bx.h"
/*----------------------------------------------------------------------------
  Define clocks
 *----------------------------------------------------------------------------*/
/* ToDo: add here your necessary defines for device initialization
         following is an example for different system frequencies */

// SYSTEM_CLOCK is defined via SYSCLK make variable
// SYSCLK_USING_IRC8M will be defined when CLKSRC=irc8m

#ifndef SYSTEM_CLOCK
#define SYSTEM_CLOCK    48000000 //48MHz
#endif

/**
 * \defgroup  XMSIS_Core_SystemAndClock   System and Clock Configuration
 * \brief Functions for system and clock setup available in system_<device>.c.
 * \details
 * Nuclei provides a template file **system_Device.c** that must be adapted by
 * the silicon vendor to match their actual device. As a <b>minimum requirement</b>,
 * this file must provide:
 *  -  A device-specific system configuration function, \ref SystemInit().
 *  -  A global variable that contains the system frequency, \ref SystemCoreClock.
 *
 * The file configures the device and, typically, initializes the oscillator (PLL) that is part
 * of the microcontroller device. This file might export other functions or variables that provide
 * a more flexible configuration of the microcontroller system.
 *
 * \note Please pay special attention to the static variable \c SystemCoreClock. This variable might be
 * used throughout the whole system initialization and runtime to calculate frequency/time related values.
 * Thus one must assure that the variable always reflects the actual system clock speed.
 *
 * \attention
 * Be aware that a value stored to \c SystemCoreClock during low level initialization (i.e. \c SystemInit()) might get
 * overwritten by C libray startup code and/or .bss section initialization.
 * Thus its highly recommended to call \ref SystemCoreClockUpdate at the beginning of the user \c main() routine.
 *
 * @{
 */

/*----------------------------------------------------------------------------
  System Core Clock Variable
 *----------------------------------------------------------------------------*/
/* ToDo: initialize SystemCoreClock with the system core clock frequency value
         achieved after system intitialization.
         This means system core clock frequency after call to SystemInit() */
/**
 * \brief      Variable to hold the system core clock value
 * \details
 * Holds the system core clock, which is the system clock frequency supplied to the SysTick
 * timer and the processor core clock. This variable can be used by debuggers to query the
 * frequency of the debug timer or to configure the trace clock speed.
 *
 * \attention
 * Compilers must be configured to avoid removing this variable in case the application
 * program is not using it. Debugging systems require the variable to be physically
 * present in memory so that it can be examined to configure the debugger.
 */

/*----------------------------------------------------------------------------
  Clock functions
 *----------------------------------------------------------------------------*/

/* system frequency define */
#define __HSI8M           (HSI8M_FREQ)            /* internal 8 MHz RC oscillator frequency */
#define __HSE             (HSE_FREQ)              /* high speed crystal oscillator frequency */
#define __SYS_OSC_CLK     (__HSI8M)               /* main oscillator frequency */

/* set the system clock frequency and declare the system clock configuration function */
volatile uint32_t SystemCoreClock = __HSI8M;


//static void system_clock_hse(void);
//static void system_clock_pll(void);


/*!
    \brief      configure the system clock
    \param[in]  none
    \param[out] none
    \retval     none
*/
void system_clock_config(void)
{
	rcu_periph_clock_enable(RCU_CLKON_UARTX);
	rcu_periph_clock_enable(RCU_CLKON_CLINT);
	rcu_periph_clock_enable(RCU_CLKON_PLIC);
	//system_clock_hse();
	//system_clock_pll();
}

/**
 * \brief      Function to Initialize the system.
 * \details
 * Initializes the microcontroller system. Typically, this function configures the
 * oscillator (PLL) that is part of the microcontroller device. For systems
 * with a variable clock speed, it updates the variable \ref SystemCoreClock.
 * SystemInit is called from the file <b>startup<i>_device</i></b>.
 */
void SystemInit(void)
{
    IBEX_EnableICache();
    /* reset the RCC clock configuration to the default reset state */
    /* enable HSI8M */
    RCU_CTL |= RCU_CTL_HSI_ON;

    /* reset SW, HPRE, PPRE bits */
    RCU_CFG0 &= ~(RCU_CFG0_SW | RCU_CFG0_HPRE | RCU_CFG0_PPRE);

    /* reset HSE_ON, PLL_ON bits */
    RCU_CTL &= ~(RCU_CTL_HSE_ON | RCU_CTL_PLL_ON);

    /* Reset HSE_BYP bit */
    RCU_CTL &= ~(RCU_CTL_HSE_BYP);

    /* reset PLL_SRC, PLL_MUL bits */

    RCU_CFG0 &= ~(RCU_CFG0_PLL_SRC | RCU_CFG0_PLL_MUL);
    RCU_CFG1 = 0x00000000U;

    /* Configure the System clock source, PLL Multiplier, AHB/APBx prescalers and Flash settings */
    system_clock_config();

    SystemCoreClockUpdate();
}

/*!
    \brief      update the SystemCoreClock with current core clock retrieved from cpu registers
    \param[in]  none
    \param[out] none
    \retval     none
*/
void SystemCoreClockUpdate(void)
{
    SystemCoreClock = rcu_clock_freq_get(CK_ROOT); 
}

//static void system_clock_hse(void)
//{
//    uint32_t timeout = 0U;
//    uint32_t rdy_flag = 0U;
//
//    /* enable HSE */
//    RCU_CTL |= RCU_CTL_HSE_ON;
//
//    /* wait until HSE is stable or the startup time is longer than HSE_STARTUP_TIMEOUT */
//    do{
//        timeout++;
//        rdy_flag = (RCU_CTL & RCU_CTL_HSE_RDY);
//    }while((0U == rdy_flag) && (HSE_STARTUP_TIMEOUT != timeout));
//
//    /* if fail */
//    if(0U == (RCU_CTL & RCU_CTL_HSE_RDY)){
//        while(1){
//        }
//    }
//
//    /* AHB = SYSCLK */
//    RCU_CFG0 |= RCU_AHB_CKROOT_DIV1;
//    /* APB = AHB/2 */
//    RCU_CFG0 |= RCU_APB_CKAHB_DIV2;
//
//    /* select HSE as system clock */
//    RCU_CFG0 |= RCU_SW_HSE;
//
//    /* wait until HSE is selected as system clock */
//    while(RCU_SWS_HSE != (RCU_CFG0 & RCU_CFG0_SWS)){
//    }
//}
//
//static void system_clock_pll(void)
//{
//    uint32_t timeout = 0U;
//    uint32_t rdy_flag = 0U;
//
//    /* enable HSE */
//    RCU_CFG0 &= ~RCU_CFG0_SW;
//    RCU_CTL |= RCU_CTL_HSE_ON;
//
//    /* wait until HSE is stable or the startup time is longer than HSE_STARTUP_TIMEOUT */
//    do{
//        timeout++;
//        rdy_flag = (RCU_CTL & RCU_CTL_HSE_RDY);
//    }while((0U == rdy_flag) && (HSE_STARTUP_TIMEOUT != timeout));
//
//    /* if fail */
//    if(0U == (RCU_CTL & RCU_CTL_HSE_RDY)){
//        while(1){
//        }
//    }
//
//    /* enable PLL */
//    RCU_CTL |= RCU_CTL_PLL_ON;
//
//    /* wait until PLL is stable or the startup time is longer than PLL_STARTUP_TIMEOUT */
//    do{
//        timeout++;
//        rdy_flag = (RCU_CTL & RCU_CTL_PLL_RDY);
//    }while((0U == rdy_flag) && (PLL_STARTUP_TIMEOUT != timeout));
//
//    /* if fail */
//    if(0U == (RCU_CTL & RCU_CTL_PLL_RDY)){
//        while(1){
//        }
//    }
//
//    /* AHB = SYSCLK */
//    RCU_CFG0 |= RCU_AHB_CKROOT_DIV1;
//    /* APB = AHB/2 */
//    RCU_CFG0 |= RCU_APB_CKAHB_DIV2;
//
//    /* select HSE as system clock */
//    RCU_CFG0 &= ~RCU_CFG0_SW;
//    RCU_CFG0 |= RCU_SW_PLL;
//
//    /* wait until HSE is selected as system clock */
//    while(RCU_SWS_PLL != (RCU_CFG0 & RCU_CFG0_SWS)){
//    }
//}



/**
 * \defgroup  XMSIS_Core_IntExcNMI_Handling   Interrupt and Exception and NMI Handling
 * \brief Functions for interrupt, exception and nmi handle available in system_<device>.c.
 * \details
 *
 * @{
 */
/** \brief Max exception handler number*/
#define MAX_SYSTEM_EXCEPTION_NUM        12
/**
 * \brief      Store the exception handlers for each exception ID
 * \note
 * - This SystemExceptionHandlers are used to store all the handlers for all
 * the exception codes Nuclei N/NX core provided.
 * - Exception code 0 - 11, totally 12 exceptions are mapped to SystemExceptionHandlers[0:11]
 * - Exception for NMI is also re-routed to exception handling(exception code 0xFFF) in startup code configuration, the handler itself is mapped to SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM]
 */
static unsigned long SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM + 1];

/**
 * \brief      Exception Handler Function Typedef
 * \note
 * This typedef is only used internal in this system_link32fa016bx.c file.
 * It is used to do type conversion for registered exception handler before calling it.
 */
typedef void (*EXC_HANDLER)(unsigned long mcause, unsigned long sp);

/**
 * \brief      System Default Exception Handler
 * \details
 * This function provided a default exception and NMI handling code for all exception ids.
 * By default, It will just print some information for debug, Vendor can customize it according to its requirements.
 */
static void system_default_exception_handler(unsigned long mcause, unsigned long sp)
{
    /* TODO: Uncomment this if you have implement printf function */
    printf("MCAUSE: 0x%lx\r\n", mcause);
    printf("MEPC  : 0x%lx\r\n", __RV_CSR_READ(CSR_MEPC));
    printf("MTVAL : 0x%lx\r\n", __RV_CSR_READ(CSR_MTVAL));
    Exception_DumpFrame(sp);
    while (1);
}

/**
 * \brief      Initialize all the default core exception handlers
 * \details
 * The core exception handler for each exception id will be initialized to \ref system_default_exception_handler.
 * \note
 * Called in \ref _init function, used to initialize default exception handlers for all exception IDs
 */
static void Exception_Init(void)
{
    for (int i = 0; i < MAX_SYSTEM_EXCEPTION_NUM + 1; i++) {
        SystemExceptionHandlers[i] = (unsigned long)system_default_exception_handler;
    }
}

/**
 * \brief       Register an exception handler for exception code EXCn
 * \details
 * * For EXCn < \ref MAX_SYSTEM_EXCEPTION_NUM, it will be registered into SystemExceptionHandlers[EXCn-1].
 * * For EXCn == NMI_EXCn, it will be registered into SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM].
 * \param   EXCn    See \ref EXCn_Type
 * \param   exc_handler     The exception handler for this exception code EXCn
 */
void Exception_Register_EXC(uint32_t EXCn, unsigned long exc_handler)
{
    if (EXCn < MAX_SYSTEM_EXCEPTION_NUM) {
        SystemExceptionHandlers[EXCn] = exc_handler;
    }
}

/**
 * \brief       Get current exception handler for exception code EXCn
 * \details
 * * For EXCn < \ref MAX_SYSTEM_EXCEPTION_NUM, it will return SystemExceptionHandlers[EXCn-1].
 * * For EXCn == NMI_EXCn, it will return SystemExceptionHandlers[MAX_SYSTEM_EXCEPTION_NUM].
 * \param   EXCn    See \ref EXCn_Type
 * \return  Current exception handler for exception code EXCn, if not found, return 0.
 */
unsigned long Exception_Get_EXC(uint32_t EXCn)
{
    if (EXCn < MAX_SYSTEM_EXCEPTION_NUM) {
        return SystemExceptionHandlers[EXCn];
    } else {
        return 0;
    }
}

/**
 * \brief      Common NMI and Exception handler entry
 * \details
 * This function provided a command entry for NMI and exception. Silicon Vendor could modify
 * this template implementation according to requirement.
 * \remarks
 * - RISCV provided common entry for all types of exception. This is proposed code template
 *   for exception entry function, Silicon Vendor could modify the implementation.
 * - For the core_exception_handler template, we provided exception register function \ref Exception_Register_EXC
 *   which can help developer to register your exception handler for specific exception number.
 */
uint32_t core_exception_handler(unsigned long mcause, unsigned long sp)
{
    uint32_t EXCn = (uint32_t)(mcause & 0X00000fff);
    EXC_HANDLER exc_handler;

    if (EXCn < MAX_SYSTEM_EXCEPTION_NUM) {
        exc_handler = (EXC_HANDLER)SystemExceptionHandlers[EXCn];
    } else {
        exc_handler = (EXC_HANDLER)system_default_exception_handler;
    }
    if (exc_handler != NULL) {
        exc_handler(mcause, sp);
    }
    return 0;
}
/** @} */ /* End of Doxygen Group XMSIS_Core_ExceptionAndNMI */

void SystemBannerPrint(void)
{
}



/**
 * \brief initialize plic config
 * \details
 * Eclic need initialize after boot up, Vendor could also change the initialization
 * configuration.
 */
void PLIC_Init(void)
{
}

/**
 * \brief      Dump Exception Frame
 * \details
 * This function provided feature to dump exception frame stored in stack.
 */
void Exception_DumpFrame(unsigned long sp)
{
    EXC_Frame_Type *exc_frame = (EXC_Frame_Type *)sp;

#ifndef __riscv_32e
    printf("ra: 0x%lx, tp: 0x%lx, t0: 0x%lx, t1: 0x%lx, t2: 0x%lx, t3: 0x%lx, t4: 0x%lx, t5: 0x%lx, t6: 0x%lx\n" \
           "a0: 0x%lx, a1: 0x%lx, a2: 0x%lx, a3: 0x%lx, a4: 0x%lx, a5: 0x%lx, a6: 0x%lx, a7: 0x%lx\n" \
           "mcause: 0x%lx, mepc: 0x%lx, mtval: 0x%lx\n", exc_frame->ra, exc_frame->tp, exc_frame->t0, \
           exc_frame->t1, exc_frame->t2, exc_frame->t3, exc_frame->t4, exc_frame->t5, exc_frame->t6, \
           exc_frame->a0, exc_frame->a1, exc_frame->a2, exc_frame->a3, exc_frame->a4, exc_frame->a5, \
           exc_frame->a6, exc_frame->a7, exc_frame->cause, exc_frame->epc, exc_frame->tval);
#else
    printf("ra: 0x%lx, tp: 0x%lx, t0: 0x%lx, t1: 0x%lx, t2: 0x%lx\n" \
           "a0: 0x%lx, a1: 0x%lx, a2: 0x%lx, a3: 0x%lx, a4: 0x%lx, a5: 0x%lx\n" \
           "mcause: 0x%lx, mepc: 0x%lx, mtval: 0x%lx\n", exc_frame->ra, exc_frame->tp, exc_frame->t0, \
           exc_frame->t1, exc_frame->t2, exc_frame->a0, exc_frame->a1, exc_frame->a2, exc_frame->a3, \
           exc_frame->a4, exc_frame->a5, exc_frame->cause, exc_frame->epc, exc_frame->tval);
#endif
}



/**
 * \brief  Initialize a specific IRQ and register the handler
 * \details
 * This function set vector mode, trigger mode and polarity, interrupt level and priority,
 * assign handler for specific IRQn.
 * \param [in]  IRQn        NMI interrupt handler address
 * \param [in]  priority    interrupt priority
 * \param [in]  handler     interrupt handler, if NULL, handler will not be installed
 * \return       -1 means invalid input parameter. 0 means successful.
 * \remarks
 * - This function use to configure specific eclic interrupt and register its interrupt handler and enable its interrupt.
 * - If the vector table is placed in read-only section(FLASHXIP mode), handler could not be installed
 */
int32_t PLIC_Register_IRQ(PLIC_IRQn_Type IRQn, uint8_t priority, PLIC_Handler_Type handler)
{
    if ((IRQn > PLIC_INT_MAX)) {
        return -1;
    }
    /* set interrupt priority */
    PLIC_SetPriorityIRQ(IRQn, priority);
    if (handler != NULL) {
        /* set interrupt handler entry to vector table */
        PLIC_SetHandler(IRQn, handler);
    }
    /* enable interrupt */
    PLIC_EnableIRQ(IRQn);
    return 0;
}
/** @} */ /* End of Doxygen Group XMSIS_Core_ExceptionAndNMI */

/**
 * \brief early init function before main
 * \details
 * This function is executed right before main function.
 * For RISC-V gnu toolchain, _init function might not be called
 * by __libc_init_array function, so we defined a new function
 * to do initialization
 */
void _premain_init(void)
{
    /* TODO: Add your own initialization code here, called before main */
//    SystemCoreClock = get_cpu_freq();
//    /* configure USART */
//    gd_com_init(SOC_DEBUG_UART);
    /* Display banner after UART initialized */
    SystemBannerPrint();
    /* Initialize exception default handlers */
    Exception_Init();
    /* PLIC initialization, mainly MTH and NLBIT */
    PLIC_Init();
}

/**
 * \brief finish function after main
 * \param [in]  status     status code return from main
 * \details
 * This function is executed right after main function.
 * For RISC-V gnu toolchain, _fini function might not be called
 * by __libc_fini_array function, so we defined a new function
 * to do initialization
 */
void _postmain_fini(int status)
{
    /* TODO: Add your own finishing code here, called after main */
}

/**
 * \brief _init function called in __libc_init_array()
 * \details
 * This `__libc_init_array()` function is called during startup code,
 * user need to implement this function, otherwise when link it will
 * error init.c:(.text.__libc_init_array+0x26): undefined reference to `_init'
 * \note
 * Please use \ref _premain_init function now
 */
void _init(void) __attribute__((constructor));
void _init(void)
{
    /* Don't put any code here, please use _premain_init now */
}

/**
 * \brief _fini function called in __libc_fini_array()
 * \details
 * This `__libc_fini_array()` function is called when exit main.
 * user need to implement this function, otherwise when link it will
 * error fini.c:(.text.__libc_fini_array+0x28): undefined reference to `_fini'
 * \note
 * Please use \ref _postmain_fini function now
 */
void _fini(void)
{
    /* Don't put any code here, please use _postmain_fini now */
}

/** @} */ /* End of Doxygen Group XMSIS_Core_SystemAndClock */

__INTERRUPT void TrapEntry_meip(void)
{
	PLIC_Handler_Entry();
}
